#!/usr/bin/python 
#from SocketServer import *
#import socket

# Convert human readable form to 32 bit value
#packed_ip = socket.inet_aton("208.146.240.1")
# Convert 32 bit value to ip adress
#ip_adress = socket.inet_ntoa(packed_ip)

import socket
import struct
import sys
import time
import SocketServer
from  threading import *
import binascii
from array import *
from datetime import datetime
#from event import event

RENDEZVOUS_REGISTER = 1 # If using 'networks' , only master peer (network owner) is kept registered all times
                    # other peers, if connected to a network, unregister themselfs from RENDEZVOUS server
RENDEZVOUS_UNREGISTER = 2
RENDEZVOUS_PEERS = 3 #List of peers . Implement 'networks' like hamachi.
RENDEZVOUS_NEW_PEERS = 4 #New peer(s) since this peer registered
#These prolly need to have IP:PORT from sender :P
RENDEZVOUS_DATA = 5 #Expect alot of data from buffer
RENDEZVOUS_PING = 6
RENDEZVOUS_ACK = 7

#Packet structure : 4:packet_size , 4:message_type [, 4:crc32 ], x:data
BUFFER_LENGTH = 1024 # overal data size is BUFFER_LENGTH - 8 (data length + message type)
    #Also, if you have more than or equal to 1024 , send an extra null sized packed so that 
    #peer would no that was the last one. 
    
    #Also implement packet ordering etc. like something tcp does. :P
RESEND_COUNT = 10

class State:
    def __init__(self,status,timeout=None,serial=None):
        self.status = status
        self.timeout = timeout
        self.time = datetime.now()
        self.count = 0
        self.serial = serial
        self.event = Event()
        
    def increment(self):
        self.count += 1
        self.time = datetime.now()
        
    def delta(self):
        return datetime.now() - self.time
        
#For peers to send/receive data from each other
class Client:
    def __init__(self,local_addr, serv_addr, name):
        self.serv_addr = (serv_addr,('0.0.0.0',0))
        self.local_addr = local_addr
        self.name = name
        self.sock = None
        self.cb_reg = None
        self.cb_conn = None
        self.cb_disconn = None
        self.cb_datarecv = None
        self.thread = None
        self.event = Event()
        self.peers = []
        self.send_state = {} #Has other peer acknowledge receiving the packet or not
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
        #self.sock.settimeout(30)
        print "Bind:",local_addr,self.sock.bind(local_addr)
        ####self.sock.connect(self.serv_addr)
        self.thread = Receiver(self.recv,self.sock)
        self.thread.start()
        
    def connect(self):
        self.send(RENDEZVOUS_REGISTER,self.encode_addr(self.local_addr),self.serv_addr)
                
    def disconnect(self):
        self.send(RENDEZVOUS_UNREGISTER,self.encode_addr(self.local_addr),self.serv_addr,False)
        #self.sock.close()
    
    def encode_addr(self,addr):
        print addr
        ip = socket.inet_aton(addr[0])
        port = struct.pack("H",addr[1])
        return  ip+port

    def decode_addr(self,addr):
        ip = socket.inet_ntoa(addr[0:4])
        port = struct.unpack("H",addr[4:6])[0]
        return  (ip,port)
        
    def get_peer_list(self):
        self.send(RENDEZVOUS_PEERS,None,self.serv_addr)
        return self.peers
        
    def send_client(self,idx,data_block):
        self.send(RENDEZVOUS_DATA, data_block, self.peers[idx])
        return
        data = ""
        size = 0
        crc = 0
        if data_block:
            data = data_block
            size = len(data)
        
        crc = binascii.crc32(data,crc)
        packed_crc = struct.pack("i",crc)
        #print "\tC CRC:", crc
        
        packed_type = struct.pack("i",RENDEZVOUS_DATA)
        data = struct.pack("i",size + struct.calcsize("iii")) + packed_type + packed_crc + data
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.sendto(data,('0.0.0.0',32324))
        
    def send(self,type,data_block,peer,wait=True):
        if not self.sock:
            return
        print "Sending data to",peer[0],len(peer), "from",self.local_addr
        
        data = ""
        size = 0
        crc = 0
        if data_block:
            data = data_block
            size = len(data)
        
        crc = binascii.crc32(data,crc)
        packed_crc = struct.pack("i",crc)
        #print "\tC CRC:", crc
        
        packed_type = struct.pack("i",type)
        data = struct.pack("i",size + struct.calcsize("iii")) + packed_type + packed_crc + data
        
        self.event.clear()
        
        count = 0
        while count < RESEND_COUNT and not self.event.isSet():
            print peer[0]
            ret = self.sock.sendto(data,peer[0]) 
            #data should be <= BUFFER_LENGTH
            count += 1
            if wait:
                self.event.wait(5)
                print "Wait ",self.local_addr,ret,self.event.isSet()
            else:
                break
        
        if count >= RESEND_COUNT and len(peer)>1 and not self.event.isSet():
            count = 0
            print "Trying local"
            while count < 3 and peer[1][1]>0:
                print "Peer",peer[1]
                ret = self.sock.sendto(data,peer[1]) #data should be <= BUFFER_LENGTH
                count += 1
                if wait:
                    self.event.wait(5)
                    print ret,self.event.isSet()
                else:
                    break
                
    def recv(self,msg,src):
            
        if len(msg)<=0:
            return
        print "\tC from",src
        try:
            length = struct.unpack("i",msg[:4])[0]
            #print "\tC Length:",length
            
            type = struct.unpack("i",msg[4:8])[0]
            #print "\tC Type:",type
            
            crc = struct.unpack("i",msg[8:12])[0]
            #print "\tC CRC:",crc
            
            if type == RENDEZVOUS_REGISTER:
                print "\tC Peer got registered",msg[12:]
                if self.cb_reg:
                    self.cb_reg(1)
                
            if type == RENDEZVOUS_UNREGISTER:
                print "\tC Peer is unregistered",msg[12:]
                if self.cb_reg:
                    self.cb_reg(0)
                    
            if type == RENDEZVOUS_PEERS:
                peer_cnt = ( length - 12 ) / 6 / 2
                print "\tC Peer list count",peer_cnt
                for i in range(0,peer_cnt):
                    i_off = (12 + i*12)
                    p_off = (12 + 4 + i*12)
                    ip = socket.inet_ntoa( msg[ i_off:i_off+4 ] )
                    port = struct.unpack("H",msg[ p_off:p_off+2 ] )[0]
                    
                    ip_local = socket.inet_ntoa( msg[ i_off+6:i_off+10 ] )
                    port_local = struct.unpack("H",msg[ p_off+6:p_off+8 ] )[0]
                    
                    self.peers.append( ((ip,port),(ip_local,port_local)) )
                #print "\t",self.peers
                
            if type == RENDEZVOUS_DATA:
                print "\tC Data:",msg[12:]
                self.send(RENDEZVOUS_ACK,'OK',(src,))
        except:
            print "Error @ recv", __str__
        
        self.event.set()



class Receiver(Thread):
    def __init__(self,cb, socket):
        Thread.__init__(self)
        self.callback = cb
        self.socket = socket
        self.setDaemon(True)
        
    def run(self):
        while True: # Receive in daemon thread
            data, address = self.socket.recvfrom(BUFFER_LENGTH)
            self.callback(data,address)
        self.socket.close()
        

#Overly complicated , just for some threading :P :D

#Server's client handler class
class Handler(Thread):
    def __init__(self,msg,address,server):
        Thread.__init__(self)
        self.msg = msg
        self.address = address
        self.server = server
        self.event = Event()
    
    def run(self):
        self.handle(self.msg,self.address)
    
    def handle(self,msg,address):
        #print msg
        length = struct.unpack("i",msg[:4])[0]
        #print "Length:",length
        type = struct.unpack("i",msg[4:8])[0]
        print "Type:",type
        crc = struct.unpack("i",msg[8:12])[0]
        #print "CRC:",crc
        
        if type == RENDEZVOUS_REGISTER:
            local = self.server.decode_addr(msg[12:18])
            print "Server register:",address,":",local
            self.register(address,local)
            self.server.send(RENDEZVOUS_REGISTER,'OK', address)
            
        if type == RENDEZVOUS_UNREGISTER:
            local = self.server.decode_addr(msg[12:18])
            print "Server unregister:",address,local
            #print "Peers:",self.peers
            self.unregister(address)
            self.server.send(RENDEZVOUS_UNREGISTER,'OK', address)
        
        if type == RENDEZVOUS_PEERS:
            data = ''
            peer_count = len(self.server.peers)
            #168 * (4 + 2 ) = 1008 bytes + 12 = 1020
            cnt_offset = 0
            cnt = 168 / 2
            if cnt+cnt_offset > len(self.server.peers):
                cnt = len(self.server.peers)
                
            for k,v in self.server.peers.iteritems():
                #remote ip
                data += socket.inet_aton(k[0]) + struct.pack("H",k[1])
                #local ip
                data += socket.inet_aton(v['localip'][0]) + struct.pack("H",v['localip'][1])
                
            self.server.send(RENDEZVOUS_PEERS, data, address)
        
        if type == RENDEZVOUS_DATA:
            print "Data received:%d bytes"%len(msg[12:])
            self.server.send(RENDEZVOUS_DATA,"OK", address,True)
            
        if type == RENDEZVOUS_ACK:
            print "Ack:",address
            if 'handler' in self.server.peers[address]:
                self.server.peers[address]['handler'].event.set()
            

    #And do some other magic if need be
    def register(self,address,local):
        self.server.peers[address] = { 'handler': self , 'localip': local  } 
        
    #And do some other magic if need be            
    def unregister(self,address):
        if self.server.peers.has_key(address) and self.server.peers[address]['localip']==local:
            if self.server.peers[address]['handler']:
                self.server.peers[address]['handler'].stop()
            del self.server.peers[address]

class Server:

    def handle(self,msg,address):
        if address in self.peers:
            if 'handler'  in self.peers[address]:
                self.peers[address]['handler'].handle(msg,address)
        else:
            handler = Handler(msg,address,self)
            #self.peers[address] = { 'handler': handler }
            handler.start()

    def encode_addr(self,addr):
        ip = socket.inet_aton(addr[0])
        port = struct.pack("H",addr[1])
        return  ip+port

    def decode_addr(self,addr):
        ip = socket.inet_ntoa(addr[0:4])
        port = struct.unpack("H",addr[4:6])[0]
        return  (ip,port)

    #Maybe move to Handler
    def send(self,type,data_block,address,wait=False):
        data = ""
        size = 0
        crc = 0
        count = 0
        if data_block:
            data = data_block
            size = len(data)
        
        crc = binascii.crc32(data,crc)
        packed_crc = struct.pack("i",crc)
        #print "CRC:", crc,"Size:",size
        
        packed_type = struct.pack("i",type)
        data = struct.pack("i",size + struct.calcsize("iii")) + packed_type + packed_crc + data

        if wait:
            while count < RESEND_COUNT:
                ret = self.socket.sendto(data,address)
                self.peers[address]['handler'].event.wait(30) #Ideally client sends ACK now
                count += 1
        else:
            ret = self.socket.sendto(data,address) #Send packet and don't give a flying fuck
            print "Server sent:",ret
            #data should be <= BUFFER_LENGTH
    
    def __init__(self,port):
        self.peers = {}
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.bind(('',port))
        self.thread = Receiver(self.handle,self.socket)
        self.thread.start()
        
    def close(self):
        self.socket.close()


if __name__ == "__main__":
    s = Server(30001)
    
    print "Client ip:",socket.gethostbyname_ex(socket.gethostname())
    c0 = Client( ('0.0.0.0',32324), ('affenas',30001),'aff')
    c0.connect()
    
    print "--------------"
    c1 = Client(('0.0.0.0',6342),('affenas',30001),'kopi')
    c1.connect()
    
    print "--------------"
    
    peers = c1.get_peer_list()
    
    print "--------------", peers
    c1.send_client(0,"LOLOLOLOLOL")
    
    print "--------------"
    c0.disconnect()
    
    print "--------------"
    c1.disconnect()
    time.sleep(1)
    s.close()
